/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved.
 */

package com.device.idea.demo.view.widget;

import static com.device.idea.demo.constant.Constant.ANCHOR_LOCATION_ARRAY_SIZE;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.PopupWindow;

import androidx.annotation.NonNull;

import com.device.idea.demo.constant.ToolbarPosition;
import com.device.idea.demo.util.LogUtil;

/**
 * 基于锚点View悬浮窗基类
 *
 * @since 2021-04-07
 */
public abstract class BaseUiPopupWindow implements IUiPopupWindow {
    /**
     * 用于承载显示View
     */
    protected PopupWindow mPopupWindow;

    /**
     * mContext 上下文对象
     */
    protected Context mContext;

    private boolean isCloseAfterSave;

    /**
     * 构造方法
     *
     * @param context 上下文对象
     * @param width popupWindow的宽
     * @param height popupWindow的高
     * @param isTouchable true-触摸popupWindow外部会消失
     */
    public BaseUiPopupWindow(@NonNull Context context, int width, int height, boolean isTouchable) {
        mContext = context;
        isCloseAfterSave = false;
        mPopupWindow = new PopupWindow(width, height);
        View contentView = onCreateContentView(context);
        mPopupWindow.setContentView(contentView);
        setOutsideTouchDismiss(isTouchable);
    }

    /**
     * 当创建内容View时
     *
     * @param context 上下文对象
     * @return 内容View
     */
    @NonNull
    protected abstract View onCreateContentView(@NonNull Context context);

    /**
     * 设置触摸popupWindow外部是否会消失
     *
     * @param isOutsideTouchable true-触摸外部会消失
     */
    protected void setOutsideTouchDismiss(boolean isOutsideTouchable) {
        if (!isOutsideTouchable) {
            mPopupWindow.setFocusable(false);
            mPopupWindow.setClippingEnabled(false);
            mPopupWindow.setOutsideTouchable(false);
            mPopupWindow.setBackgroundDrawable(null);
            mPopupWindow.getContentView().setFocusable(true);
            mPopupWindow.getContentView().setFocusableInTouchMode(true);
            mPopupWindow.getContentView().setOnKeyListener((view, keyCode, event) -> {
                if (keyCode == KeyEvent.KEYCODE_BACK) {
                    mPopupWindow.dismiss();
                    return true;
                }
                return false;
            });
        } else {
            mPopupWindow.setClippingEnabled(false);
            mPopupWindow.setOutsideTouchable(true);
            mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        }
    }

    /**
     * 显示，相对偏移x,y
     *
     * @param anchorView 锚点视图
     * @param toolbarPosition 工具栏位置
     * @param offsetX x轴方向偏移
     * @param offsetY y轴方向偏移
     */
    protected void showAtAnchorView(@NonNull View anchorView, @NonNull ToolbarPosition toolbarPosition, int offsetX,
        int offsetY) {
        switch (toolbarPosition) {
            case BOTTOM:
                showAtAnchorView(anchorView, IUiPopupWindow.CENTER, IUiPopupWindow.VERTICAL_ABOVE, offsetX, offsetY);
                break;
            case BOTTOM_END:
                showAtAnchorView(anchorView, IUiPopupWindow.HORIZONTAL_START, IUiPopupWindow.VERTICAL_ABOVE, offsetX,
                    offsetY);
                break;
            case TOP:
                showAtAnchorView(anchorView, IUiPopupWindow.CENTER, IUiPopupWindow.VERTICAL_BELOW, offsetX, offsetY);
                break;
            case START:
                showAtAnchorView(anchorView, IUiPopupWindow.HORIZONTAL_END, IUiPopupWindow.CENTER, offsetX, offsetY);
                break;
            case END:
                showAtAnchorView(anchorView, IUiPopupWindow.HORIZONTAL_START, IUiPopupWindow.CENTER, offsetX, offsetY);
                break;
            case TOP_END:
                showAtAnchorView(anchorView, IUiPopupWindow.HORIZONTAL_END, IUiPopupWindow.VERTICAL_BELOW, offsetX,
                    offsetY);
                break;
            default:
                break;
        }
    }

    /**
     * 基于锚点View显示
     *
     * @param anchorView 锚点View
     * @param horizontalPos 水平方向位置
     * @param verticalPos 垂直方向位置
     * @param offsetX 水平方向的偏移
     * @param offsetY 垂直方向的偏移
     */
    private void showAtAnchorView(@NonNull View anchorView, @HorizontalPosition int horizontalPos,
        @VerticalPosition int verticalPos, int offsetX, int offsetY) {
        if (mPopupWindow.isShowing()) {
            LogUtil.info(getTag(), "showAtAnchorView: update");
            mPopupWindow.update();
            return;
        }
        LogUtil.info(getTag(),
            "showAtAnchorView" + "anchorView =" + anchorView + " offsetX =" + offsetX + ",offsetY =" + offsetY);
        final View contentView = mPopupWindow.getContentView();
        contentView.measure(makeDropDownMeasureSpec(mPopupWindow.getWidth()),
            makeDropDownMeasureSpec(mPopupWindow.getHeight()));
        final int measuredW = contentView.getMeasuredWidth();
        final int measuredH = contentView.getMeasuredHeight();
        final int[] anchorLocation = new int[ANCHOR_LOCATION_ARRAY_SIZE]; // 存储X和Y坐标的数组
        anchorView.getLocationOnScreen(anchorLocation);
        int locationX = calculateOffsetX(anchorView, horizontalPos, measuredW, anchorLocation[0] + offsetX);
        int locationY =
            calculateOffsetY(anchorView, verticalPos, measuredH, anchorLocation[1] + anchorView.getHeight() + offsetY);
        mPopupWindow.showAtLocation(anchorView, Gravity.NO_GRAVITY, locationX, locationY);
    }

    /**
     * 计算水平方向上偏移量
     *
     * @param anchor 参照view
     * @param horizontalGravity 水平方向上的Gravity
     * @param measuredW 测量出内容view的宽
     * @param defaultOffsetX 默认X偏移量
     * @return X偏移量
     */
    private int calculateOffsetX(@NonNull View anchor, @HorizontalPosition int horizontalGravity, int measuredW,
        int defaultOffsetX) {
        int offsetX = defaultOffsetX;
        switch (horizontalGravity) {
            case IUiPopupWindow.HORIZONTAL_START:
                return offsetX - measuredW;
            case IUiPopupWindow.HORIZONTAL_ALIGN_END:
                return offsetX - measuredW + anchor.getWidth();
            case IUiPopupWindow.CENTER:
                return offsetX + anchor.getWidth() / 2 - measuredW / 2; // 以水平为中心,获取X偏移量
            case IUiPopupWindow.HORIZONTAL_END:
                return offsetX + anchor.getWidth();
            default:
                return offsetX;
        }
    }

    /**
     * 计算竖直方向上偏移量
     *
     * @param anchor 参照view
     * @param verticalGravity 竖直方向上的Gravity
     * @param measuredH 测量出内容view的高
     * @param defaultOffsetY 默认Y偏移量
     * @return Y偏移量
     */
    private int calculateOffsetY(@NonNull View anchor, @VerticalPosition int verticalGravity, int measuredH,
        int defaultOffsetY) {
        switch (verticalGravity) {
            case IUiPopupWindow.VERTICAL_ABOVE:
                return defaultOffsetY - measuredH - anchor.getHeight();
            case IUiPopupWindow.VERTICAL_ALIGN_BOTTOM:
                return defaultOffsetY - measuredH;
            case IUiPopupWindow.CENTER:
                return defaultOffsetY - anchor.getHeight() / 2 - measuredH / 2; // 以水平为中心,获取Y偏移量
            case IUiPopupWindow.VERTICAL_ALIGN_TOP:
                return defaultOffsetY - anchor.getHeight();
            default:
                return defaultOffsetY;
        }
    }

    private static int makeDropDownMeasureSpec(int measureSpec) {
        return View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(measureSpec),
            getDropDownMeasureSpecMode(measureSpec));
    }

    private static int getDropDownMeasureSpecMode(int measureSpec) {
        if (measureSpec == ViewGroup.LayoutParams.WRAP_CONTENT) {
            return View.MeasureSpec.UNSPECIFIED;
        }
        return View.MeasureSpec.EXACTLY;
    }

    @Override
    public void dismiss() {
        LogUtil.info(getTag(), "dismiss");
        mPopupWindow.dismiss();
    }

    @Override
    public void setIsCloseAfterSave(boolean isCloseAfterSave) {
        this.isCloseAfterSave = isCloseAfterSave;
    }

    /**
     * 获取日志TAG
     *
     * @return 日志TAG
     */
    protected abstract String getTag();
}
